﻿using System;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestListGenerator;
using TestListGenerator.Common;

namespace TestListGenerator.Tests
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class ReflectionHelperTests
    {
        public ReflectionHelperTests()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

 
        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region MethodIsATest
        [TestMethod]
        public void TestMethodIsATest_Null()
        {
            try
            {
                ReflectionHelper.MethodIsATest(null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullException was not thrown when passing in null to MethodIsATest");
                return;
            }
            Assert.Fail("An ArgumentNullException was not thrown when passing in null to MethodIsATest");
        }

        [TestMethod]
        public void TestMethodIsATest_True()
        {
            MethodInfo mi = TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "NoCategories");
            Assert.IsTrue(ReflectionHelper.MethodIsATest(mi));
        }

        [TestMethod]
        public void TestMethodIsATest_False()
        {
            MethodInfo mi = TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "NotATest");
            Assert.IsFalse(ReflectionHelper.MethodIsATest(mi));
        }
        #endregion

        #region GetTestCategories

        [TestMethod]
        public void TestGetTestCategories_Null()
        {
            try
            {
                ReflectionHelper.GetTestCategories(null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullException was not thrown when passing in null to GetTestCategories");
                return;
            }
            Assert.Fail("An ArgumentNullException was not thrown when passing in null to GetTestCategories");
        }

        [TestMethod]
        public void TestGetTestCategories_Class1()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "OneCategory"));
            Assert.AreEqual(1, categories.Count, "We only expected one item to be in the hashset");
            Assert.IsTrue(categories.Contains("onecategory"), "We expected to see onecategory in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_Class2()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_2, "OneCategory"));
            Assert.AreEqual(1, categories.Count, "We only expected one item to be in the hashset");
            Assert.IsTrue(categories.Contains("testsubject2"), "We expected to see testsubject2 in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_NoCategories()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "NoCategories"));
            Assert.AreEqual(0, categories.Count, "We didn't expect any categories");
        }

        [TestMethod]
        public void TestGetTestCategories_MultipleCategories()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "MultipleCategories"));
            Assert.AreEqual(3, categories.Count, "We expected three items to be in the hashset");
            Assert.IsTrue(categories.Contains("category1"), "We expected to see category1 in the hashset");
            Assert.IsTrue(categories.Contains("category2"), "We expected to see category2 in the hashset");
            Assert.IsTrue(categories.Contains("category3"), "We expected to see category3 in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_RepeatCategories()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "RepeatedCategories"));
            Assert.AreEqual(2, categories.Count, "We expected two items to be in the hashset");
            Assert.IsTrue(categories.Contains("category1"), "We expected to see category1 in the hashset");
            Assert.IsTrue(categories.Contains("category2"), "We expected to see category2 in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_Class1_CustomAttributes()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "OneCategory_CustomAttribute"));
            Assert.AreEqual(1, categories.Count, "We only expected one item to be in the hashset");
            Assert.IsTrue(categories.Contains("onecategory_customattribute"), "We expected to see onecategory_customattribute in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_Class_CustomAttribtue()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_2, "OneCategory_CustomAttribute"));
            Assert.AreEqual(1, categories.Count, "We only expected one item to be in the hashset");
            Assert.IsTrue(categories.Contains("testsubject2_customattribute"), "We expected to see testsubject2_customattribute in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_MultipleCategories_CustomAttribute()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "MultipleCategories_CustomAttribute"));
            Assert.AreEqual(3, categories.Count, "We expected three items to be in the hashset");
            Assert.IsTrue(categories.Contains("category1_customattribute"), "We expected to see category1_customattribute in the hashset");
            Assert.IsTrue(categories.Contains("category2_customattribute"), "We expected to see category2_customattribute in the hashset");
            Assert.IsTrue(categories.Contains("category3_customattribute"), "We expected to see category3_customattribute in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_RepeatCategories_CustomAttribute()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "RepeatedCategories_CustomAttribute"));
            Assert.AreEqual(2, categories.Count, "We expected two items to be in the hashset");
            Assert.IsTrue(categories.Contains("category1_customattribute"), "We expected to see category1_customattribute in the hashset");
            Assert.IsTrue(categories.Contains("category2_customattribute"), "We expected to see category2_customattribute in the hashset");
        }

        [TestMethod]
        public void TestGetTestCategories_MixedCategories()
        {
            HashSet<string> categories = ReflectionHelper.GetTestCategories(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "MixedCategories"));
            Assert.AreEqual(2, categories.Count, "We expected two items to be in the hashset");
            Assert.IsTrue(categories.Contains("testproperty"), "We expected to see testproperty in the hashset");
            Assert.IsTrue(categories.Contains("testcategory"), "We expected to see testcategory in the hashset");
        }
        #endregion

        #region GetTestsFromAnAssembly
        [TestMethod]
        public void TestGetTestsFromAnAssembly_Null()
        {
            try
            {
                ReflectionHelper.GetTestsFromAssembly(null);
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(ArgumentNullException), ex, "An ArgumentNullException was not thrown when passing null into GetTestsFromAnAssembly");
                return;
            }
            Assert.Fail("An ArgumentNullException was not thrown when passing null into GetTestsFromAnAssembly");
        }
        [TestMethod]
        public void TestGetTestsFromAnAssembly_InvalidPath()
        {
            try
            {
                ReflectionHelper.GetTestsFromAssembly(@"C:\A Directory That Does Not Exist\And Neither\Does\ThisFile.txt");
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(System.IO.FileNotFoundException), ex, "A FileNotFound exception was not thrown when loading an invalid file path");
                return;
            }
            Assert.Fail("A FileNotFound exception was not thrown when loading an invalid file path");
        }
        [TestMethod]
        public void TestGetTestsFromAnAssembly_InvalidFileType()
        {
            try
            {
                ReflectionHelper.GetTestsFromAssembly(System.Environment.SystemDirectory + "\\appwiz.cpl");
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(BadImageFormatException), ex, "A BadImageFormatException was not thrown when loading an invalid file type");
                return;
            }
            Assert.Fail("A BadImageFormatException was not thrown when loading an invalid file type");

        }

        [TestMethod]
        public void TestGetTestsFromAnAssembly_NonDotNetDll() 
        {
            try
            {
                ReflectionHelper.GetTestsFromAssembly(System.Environment.SystemDirectory + "\\comctl32.dll");
            }
            catch (Exception ex)
            {
                TestHelper.CheckException(typeof(BadImageFormatException), ex, "A BadImageFormatException was not thrown when loading a non .net dll");
                return;
            }
            Assert.Fail("A BadImageFormatException was not thrown when loading a non .net dll");
        }
        [TestMethod]
        public void TestGetTestsFromAnAssmebly_Valid()
        {
            // Grab the test method categories
            Dictionary<MethodInfo, HashSet<string>> results = ReflectionHelper.GetTestsFromAssembly("TestListGenerator.Tests.TestSubject.dll");

            // Check we got back the right amount of tests
            Assert.AreEqual(15, results.Count, "We got back a different number of tests than we were expecting");

            // Get a count of all the category / test associations
            Assert.AreEqual(22, results.Sum(f => f.Value.Count), "The number of category to test associations was not what we expected");

            // Try a few associations
            // Non Test Method
            Assert.IsFalse(results.ContainsKey(TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "NotATestWithAProperty")), "TestSubject1.NotATestWithAProperty() was in the result, even though it is not a TestMethod");

            // No Categories
            HashSet<string> categories = results[TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "NoCategories")];
            Assert.AreEqual(0, categories.Count, "There was more than 0 categories associated with the TestSubject1.NoCategories()");

            // One Category
            categories = results[TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "OneCategory_CustomAttribute")];
            Assert.AreEqual(1, categories.Count, "There should have only been 1 category assocaited with TestSubject1.OneCategory_CustomAttribute()");
            Assert.IsTrue(categories.Contains("onecategory_customattribute"), "The only category should have been OneCategory_CustomAttribute for TestSubject1.OneCategory_CustomAttribute()");

            // Multiple Categories
            categories = results[TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_1, "RepeatedCategories_CustomAttribute")];
            Assert.AreEqual(2, categories.Count, "There should have been 2 categories assocaited with TestSubject1.RepeatedCategories_CustomAttribute()");
            Assert.IsTrue(categories.Contains("category1_customattribute"), "The category Category1_CustomAttribute was not associated to TestSubject1.RepeatedCategories_CustomAttribute()");
            Assert.IsTrue(categories.Contains("category2_customattribute"), "The category Category2_CustomAttribute was not associated to TestSubject1.RepeatedCategories_CustomAttribute()");

            // Another Class
            categories = results[TestObjects.GetMethod(TestObjects.TEST_SUBJECT_CLASS_2, "OneCategory")];
            Assert.AreEqual(1, categories.Count, "There should have only been 1 category assocaited TestSubject2.OneCategory()");
            Assert.IsTrue(categories.Contains("testsubject2"), "The only category should have been TestSubject2 for TestSubject2.OneCategory()");

        }
        #endregion
    }

}
